home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / PEAR / PackageFileManager.php < prev    next >
PHP Script  |  2004-10-01  |  64KB  |  1,418 lines

  1. <?php
  2. //
  3. // +------------------------------------------------------------------------+
  4. // | PEAR :: Package File Manager                                           |
  5. // +------------------------------------------------------------------------+
  6. // | Copyright (c) 2003-2004 Gregory Beaver                                 |
  7. // | Email         cellog@phpdoc.org                                        |
  8. // +------------------------------------------------------------------------+
  9. // | This source file is subject to version 3.00 of the PHP License,        |
  10. // | that is available at http://www.php.net/license/3_0.txt.               |
  11. // | If you did not receive a copy of the PHP license and are unable to     |
  12. // | obtain it through the world-wide-web, please send a note to            |
  13. // | license@php.net so we can mail you a copy immediately.                 |
  14. // +------------------------------------------------------------------------+
  15. // | Portions of this code based on phpDocumentor                           |
  16. // | Web           http://www.phpdoc.org                                    |
  17. // | Mirror        http://phpdocu.sourceforge.net/                          |
  18. // +------------------------------------------------------------------------+
  19. // $Id: PackageFileManager.php,v 1.34 2004/02/19 14:49:49 cellog Exp $
  20. //
  21.  
  22. /**
  23.  * @package PEAR_PackageFileManager
  24.  */
  25. /**
  26.  * PEAR installer
  27.  */
  28. require_once 'PEAR/Common.php';
  29. /**#@+
  30.  * Error Codes
  31.  */
  32. define('PEAR_PACKAGEFILEMANAGER_NOSTATE', 1);
  33. define('PEAR_PACKAGEFILEMANAGER_NOVERSION', 2);
  34. define('PEAR_PACKAGEFILEMANAGER_NOPKGDIR', 3);
  35. define('PEAR_PACKAGEFILEMANAGER_NOBASEDIR', 4);
  36. define('PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND', 5);
  37. define('PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND_ANYWHERE', 6);
  38. define('PEAR_PACKAGEFILEMANAGER_CANTWRITE_PKGFILE', 7);
  39. define('PEAR_PACKAGEFILEMANAGER_DEST_UNWRITABLE', 8);
  40. define('PEAR_PACKAGEFILEMANAGER_CANTCOPY_PKGFILE', 9);
  41. define('PEAR_PACKAGEFILEMANAGER_CANTOPEN_TMPPKGFILE', 10);
  42. define('PEAR_PACKAGEFILEMANAGER_PATH_DOESNT_EXIST', 11);
  43. define('PEAR_PACKAGEFILEMANAGER_NOCVSENTRIES', 12);
  44. define('PEAR_PACKAGEFILEMANAGER_DIR_DOESNT_EXIST', 13);
  45. define('PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS', 14);
  46. define('PEAR_PACKAGEFILEMANAGER_NOPACKAGE', 15);
  47. define('PEAR_PACKAGEFILEMANAGER_WRONG_MROLE', 16);
  48. define('PEAR_PACKAGEFILEMANAGER_NOSUMMARY', 17);
  49. define('PEAR_PACKAGEFILEMANAGER_NODESC', 18);
  50. define('PEAR_PACKAGEFILEMANAGER_ADD_MAINTAINERS', 19);
  51. define('PEAR_PACKAGEFILEMANAGER_NO_FILES', 20);
  52. define('PEAR_PACKAGEFILEMANAGER_IGNORED_EVERYTHING', 21);
  53. define('PEAR_PACKAGEFILEMANAGER_INVALID_PACKAGE', 22);
  54. define('PEAR_PACKAGEFILEMANAGER_INVALID_REPLACETYPE', 23);
  55. define('PEAR_PACKAGEFILEMANAGER_INVALID_ROLE', 24);
  56. define('PEAR_PACKAGEFILEMANAGER_PHP_NOT_PACKAGE', 25);
  57. define('PEAR_PACKAGEFILEMANAGER_CVS_PACKAGED', 26);
  58. /**#@-*/
  59. /**
  60.  * Error messages
  61.  * @global array $GLOBALS['_PEAR_PACKAGEFILEMANAGER_ERRORS']
  62.  */
  63. $GLOBALS['_PEAR_PACKAGEFILEMANAGER_ERRORS'] =
  64. array(
  65.     'en' =>
  66.     array(
  67.         PEAR_PACKAGEFILEMANAGER_NOSTATE =>
  68.             'Release State (option \'state\') must by specified in PEAR_PackageFileManager setOptions (alpha|beta|stable)',
  69.         PEAR_PACKAGEFILEMANAGER_NOVERSION =>
  70.             'Release Version (option \'version\') must be specified in PEAR_PackageFileManager setOptions',
  71.         PEAR_PACKAGEFILEMANAGER_NOPKGDIR =>
  72.             'Package source base directory (option \'packagedirectory\') must be ' .
  73.             'specified in PEAR_PackageFileManager setOptions',
  74.         PEAR_PACKAGEFILEMANAGER_NOBASEDIR =>
  75.             'Package install base directory (option \'baseinstalldir\') must be ' .
  76.             'specified in PEAR_PackageFileManager setOptions',
  77.         PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND =>
  78.             'Base class "%s" can\'t be located',
  79.         PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND_ANYWHERE =>
  80.             'Base class "%s" can\'t be located in default or user-specified directories',
  81.         PEAR_PACKAGEFILEMANAGER_CANTWRITE_PKGFILE =>
  82.             'Failed to write package.xml file to destination directory',
  83.         PEAR_PACKAGEFILEMANAGER_DEST_UNWRITABLE =>
  84.             'Destination directory "%s" is unwritable',
  85.         PEAR_PACKAGEFILEMANAGER_CANTCOPY_PKGFILE =>
  86.             'Failed to copy package.xml.tmp file to package.xml',
  87.         PEAR_PACKAGEFILEMANAGER_CANTOPEN_TMPPKGFILE =>
  88.             'Failed to open temporary file "%s" for writing',
  89.         PEAR_PACKAGEFILEMANAGER_PATH_DOESNT_EXIST =>
  90.             'package.xml file path "%s" doesn\'t exist or isn\'t a directory',
  91.         PEAR_PACKAGEFILEMANAGER_NOCVSENTRIES =>
  92.             'Directory "%s" is not a CVS directory (it must have the CVS/Entries file)',
  93.         PEAR_PACKAGEFILEMANAGER_DIR_DOESNT_EXIST =>
  94.             'Package source base directory "%s" doesn\'t exist or isn\'t a directory',
  95.         PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS =>
  96.             'Run $managerclass->setOptions() before any other methods',
  97.         PEAR_PACKAGEFILEMANAGER_NOPACKAGE =>
  98.             'Package Name (option \'package\') must by specified in PEAR_PackageFileManager '.
  99.             'setOptions to create a new package.xml',
  100.         PEAR_PACKAGEFILEMANAGER_NOSUMMARY =>
  101.             'Package Summary (option \'summary\') must by specified in PEAR_PackageFileManager' .
  102.             ' setOptions to create a new package.xml',
  103.         PEAR_PACKAGEFILEMANAGER_NODESC =>
  104.             'Detailed Package Description (option \'description\') must be' .
  105.             ' specified in PEAR_PackageFileManager setOptions to create a new package.xml',
  106.         PEAR_PACKAGEFILEMANAGER_WRONG_MROLE =>
  107.             'Maintainer role must be one of "%s", was "%s"',
  108.         PEAR_PACKAGEFILEMANAGER_ADD_MAINTAINERS =>
  109.             'Add maintainers to a package before generating the package.xml',
  110.         PEAR_PACKAGEFILEMANAGER_NO_FILES =>
  111.             'No files found, check the path "%s"',
  112.         PEAR_PACKAGEFILEMANAGER_IGNORED_EVERYTHING =>
  113.             'No files left, check the path "%s" and ignore option "%s"',
  114.         PEAR_PACKAGEFILEMANAGER_INVALID_PACKAGE =>
  115.             'Package validation failed:%s%s',
  116.         PEAR_PACKAGEFILEMANAGER_INVALID_REPLACETYPE =>
  117.             'Replacement Type must be one of "%s", was passed "%s"',
  118.         PEAR_PACKAGEFILEMANAGER_INVALID_ROLE =>
  119.             'Invalid file role passed to addRole, must be one of "%s", was passed "%s"',
  120.         PEAR_PACKAGEFILEMANAGER_PHP_NOT_PACKAGE =>
  121.             'addDependency had PHP as a package, use type="php"',
  122.         PEAR_PACKAGEFILEMANAGER_CVS_PACKAGED =>
  123.             'path "%path%" contains CVS directory',
  124.         ),
  125.         // other language translations go here
  126.      );
  127. /**
  128.  * PEAR :: PackageFileManager updates the <filelist></filelist> section
  129.  * of a PEAR package.xml file to reflect the current files in
  130.  * preparation for a release.
  131.  *
  132.  * The PEAR_PackageFileManager class uses a plugin system to generate the
  133.  * list of files in a package.  This allows both standard recursive
  134.  * directory parsing (plugin type file) and more intelligent options
  135.  * such as the CVS browser {@link PEAR_PackageFileManager_Cvs}, which
  136.  * grabs all files in a local CVS checkout to create the list, ignoring
  137.  * any other local files.
  138.  *
  139.  * Other options include specifying roles for file extensions (all .php
  140.  * files are role="php", for example), roles for directories (all directories
  141.  * named "tests" are given role="tests" by default), and exceptions.
  142.  * Exceptions are specific pathnames with * and ? wildcards that match
  143.  * a default role, but should have another.  For example, perhaps
  144.  * a debug.tpl template would normally be data, but should be included
  145.  * in the docs role.  Along these lines, to exclude files entirely,
  146.  * use the ignore option.
  147.  *
  148.  * Required options for a release include version, baseinstalldir, state,
  149.  * and packagedirectory (the full path to the local location of the
  150.  * package to create a package.xml file for)
  151.  *
  152.  * Example usage:
  153.  * <code>
  154.  * <?php
  155.  * require_once('PEAR/PackageFileManager.php');
  156.  * $packagexml = new PEAR_PackageFileManager;
  157.  * $e = $packagexml->setOptions(
  158.  * array('baseinstalldir' => 'PhpDocumentor',
  159.  *  'version' => '1.2.1',
  160.  *  'packagedirectory' => 'C:/Web Pages/chiara/phpdoc2/',
  161.  *  'state' => 'stable',
  162.  *  'filelistgenerator' => 'cvs', // generate from cvs, use file for directory
  163.  *  'notes' => 'We\'ve implemented many new and exciting features',
  164.  *  'ignore' => array('TODO', 'tests/'), // ignore TODO, all files in tests/
  165.  *  'installexceptions' => array('phpdoc' => '/*'), // baseinstalldir ="/" for phpdoc
  166.  *  'dir_roles' => array('tutorials' => 'doc'),
  167.  *  'exceptions' => array('README' => 'doc', // README would be data, now is doc
  168.  *                        'PHPLICENSE.txt' => 'doc'))); // same for the license
  169.  * if (PEAR::isError($e)) {
  170.  *     echo $e->getMessage();
  171.  *     die();
  172.  * }
  173.  * $e = $test->addPlatformException('pear-phpdoc.bat', 'windows');
  174.  * if (PEAR::isError($e)) {
  175.  *     echo $e->getMessage();
  176.  *     exit;
  177.  * }
  178.  * $packagexml->addRole('pkg', 'doc'); // add a new role mapping
  179.  * if (PEAR::isError($e)) {
  180.  *     echo $e->getMessage();
  181.  *     exit;
  182.  * }
  183.  * // replace @PHP-BIN@ in this file with the path to php executable!  pretty neat
  184.  * $e = $test->addReplacement('pear-phpdoc', 'pear-config', '@PHP-BIN@', 'php_bin');
  185.  * if (PEAR::isError($e)) {
  186.  *     echo $e->getMessage();
  187.  *     exit;
  188.  * }
  189.  * $e = $test->addReplacement('pear-phpdoc.bat', 'pear-config', '@PHP-BIN@', 'php_bin');
  190.  * if (PEAR::isError($e)) {
  191.  *     echo $e->getMessage();
  192.  *     exit;
  193.  * }
  194.  * // note use of {@link debugPackageFile()} - this is VERY important
  195.  * if (isset($_GET['make']) || (isset($_SERVER['argv'][2]) &&
  196.  *       $_SERVER['argv'][2] == 'make')) {
  197.  *     $e = $packagexml->writePackageFile();
  198.  * } else {
  199.  *     $e = $packagexml->debugPackageFile();
  200.  * }
  201.  * if (PEAR::isError($e)) {
  202.  *     echo $e->getMessage();
  203.  *     die();
  204.  * }
  205.  * ?>
  206.  * </code>
  207.  * 
  208.  * In addition, a package.xml file can now be generated from
  209.  * scratch, with the usage of new options package, summary, description, and
  210.  * the use of the {@link addMaintainer()} method
  211.  * @package PEAR_PackageFileManager
  212.  */
  213. class PEAR_PackageFileManager
  214. {
  215.     /**
  216.      * Format: array(array(regexp-ready string to search for whole path,
  217.      * regexp-ready string to search for basename of ignore strings),...)
  218.      * @var false|array
  219.      * @access private
  220.      */
  221.     var $_ignore = false;
  222.     
  223.     /**
  224.      * Contents of the package.xml file
  225.      * @var string
  226.      * @access private
  227.      */
  228.     var $_packageXml = false;
  229.     
  230.     /**
  231.      * Contents of the original package.xml file, if any
  232.      * @var string
  233.      * @access private
  234.      */
  235.     var $_oldPackageXml = false;
  236.     
  237.     /**
  238.      * @access private
  239.      * @var PEAR_Common
  240.      */
  241.     var $_pear;
  242.     
  243.     /**
  244.      * @access private
  245.      * @var array
  246.      */
  247.     var $_warningStack = array();
  248.     
  249.     /**
  250.      * @access private
  251.      * @var string
  252.      */
  253.     var $_options = array(
  254.                       'packagefile' => 'package.xml',
  255.                       'doctype' => 'http://pear.php.net/dtd/package-1.0',
  256.                       'filelistgenerator' => 'file',
  257.                       'license' => 'PHP License',
  258.                       'changelogoldtonew' => true,
  259.                       'roles' =>
  260.                         array(
  261.                             'php' => 'php',
  262.                             'html' => 'doc',
  263.                             '*' => 'data',
  264.                              ),
  265.                       'dir_roles' =>
  266.                         array(
  267.                             'docs' => 'doc',
  268.                             'examples' => 'doc',
  269.                             'tests' => 'test',
  270.                              ),
  271.                       'exceptions' => array(),
  272.                       'installexceptions' => array(),
  273.                       'installas' => array(),
  274.                       'platformexceptions' => array(),
  275.                       'scriptphaseexceptions' => array(),
  276.                       'ignore' => array(),
  277.                       'include' => false,
  278.                       'deps' => false,
  279.                       'maintainers' => false,
  280.                       'notes' => '',
  281.                       'changelognotes' => false,
  282.                       'outputdirectory' => false,
  283.                       'pathtopackagefile' => false,
  284.                       'lang' => 'en',
  285.                       'configure_options' => array(),
  286.                       'replacements' => array(),
  287.                       'pearcommonclass' => false,
  288.                       'simpleoutput' => false,
  289.                       'addhiddenfiles' => false,
  290.                       );
  291.     
  292.     /**
  293.      * Does nothing, use setOptions
  294.      *
  295.      * The constructor is not used in order to be able to
  296.      * return a PEAR_Error from setOptions
  297.      * @see setOptions()
  298.      */
  299.     function PEAR_PackageFileManager()
  300.     {
  301.     }
  302.     
  303.     /**
  304.      * Set package.xml generation options
  305.      *
  306.      * The options array is indexed as follows:
  307.      * <code>
  308.      * $options = array('option_name' => <optionvalue>);
  309.      * </code>
  310.      *
  311.      * The documentation below simplifies this description through
  312.      * the use of option_name without quotes
  313.      *
  314.      * Configuration options:
  315.      * - lang: lang controls the language in which error messages are
  316.      *         displayed.  There are currently only English error messages,
  317.      *         but any contributed will be added over time.<br>
  318.      *         Possible values: en (default)
  319.      * - packagefile: the name of the packagefile, defaults to package.xml
  320.      * - pathtopackagefile: the path to an existing package file to read in,
  321.      *                      if different from the packagedirectory
  322.      * - packagedirectory: the path to the base directory of the package.  For
  323.      *                     package PEAR_PackageFileManager, this path is
  324.      *                     /path/to/pearcvs/pear/PEAR_PackageFileManager where
  325.      *                     /path/to/pearcvs is a local path on your hard drive
  326.      * - outputdirectory: the path in which to place the generated package.xml
  327.      *                    by default, this is ignored, and the package.xml is
  328.      *                    created in the packagedirectory
  329.      * - filelistgenerator: the <filelist> section plugin which will be used.
  330.      *                      In this release, there are two generator plugins,
  331.      *                      file and cvs.  For details, see the docs for these
  332.      *                      plugins
  333.      * - usergeneratordir: For advanced users.  If you write your own filelist
  334.      *                     generator plugin, use this option to tell
  335.      *                     PEAR_PackageFileManager where to find the file that
  336.      *                     contains it.  If the plugin is named foo, the class
  337.      *                     must be named PEAR_PackageFileManager_Foo
  338.      *                     no matter where it is located.  By default, the Foo
  339.      *                     plugin is located in PEAR/PackageFileManager/Foo.php.
  340.      *                     If you pass /path/to/foo in this option, setOptions
  341.      *                     will look for PEAR_PackageFileManager_Foo in
  342.      *                     /path/to/foo/Foo.php
  343.      * - doctype: Specifies the DTD of the package.xml file.  Default is
  344.      *            http://pear.php.net/dtd/package-1.0
  345.      * - pearcommonclass: Specifies the name of the class to instantiate, default
  346.      *                    is PEAR_Common, but users can override this with a custom
  347.      *                    class that implements PEAR_Common's method interface
  348.      * - changelogoldtonew: True if the ChangeLog should list from oldest entry to
  349.      *                      newest.  Set to false if you would like new entries first
  350.      * - simpleoutput: True if the package.xml should not contain md5sum or <provides />
  351.      *                 for readability
  352.      * - addhiddenfiles: True if you wish to add hidden files/directories that begin with .
  353.      *                   like .bashrc.  This is only used by the File generator.  The CVS
  354.      *                   generator will use all files in CVS regardless of format
  355.      *
  356.      * package.xml simple options:
  357.      * - baseinstalldir: The base directory to install this package in.  For
  358.      *                   package PEAR_PackageFileManager, this is "PEAR", for
  359.      *                   package PEAR, this is "/"
  360.      * - license: The license this release is released under.  Default is
  361.      *            PHP License if left unspecified
  362.      * - notes: Release notes, any text describing what makes this release unique
  363.      * - changelognotes: notes for the changelog, this should be more detailed than
  364.      *                   the release notes.  By default, PEAR_PackageFileManager uses
  365.      *                   the notes option for the changelog as well
  366.      * - version: The version number for this release.  Remember the convention for
  367.      *            numbering: initial alpha is between 0 and 1, add b<beta number> for
  368.      *            beta as in 1.0b1, the integer portion of the version should specify
  369.      *            backwards compatibility, as in 1.1 is backwards compatible with 1.0,
  370.      *            but 2.0 is not backwards compatible with 1.10.  Also note that 1.10
  371.      *            is a greater release version than 1.1 (think of it as "one point ten"
  372.      *            and "one point one").  Bugfix releases should be a third decimal as in
  373.      *            1.0.1, 1.0.2
  374.      * - package: [optional] Package name.  Use this to create a new package.xml, or
  375.      *            overwrite an existing one from another package used as a template
  376.      * - summary: [optional] Summary of package purpose
  377.      * - description: [optional] Description of package purpose.  Note that the above
  378.      *                three options are not optional when creating a new package.xml
  379.      *                from scratch
  380.      *
  381.      * <b>WARNING</b>: all complex options that require a file path are case-sensitive
  382.      *
  383.      * package.xml complex options:
  384.      * - ignore: an array of filenames, directory names, or wildcard expressions specifying
  385.      *           files to exclude entirely from the package.xml.  Wildcards are operating system
  386.      *           wildcards * and ?.  file*foo.php will exclude filefoo.php, fileabrfoo.php and
  387.      *           filewho_is_thisfoo.php.  file?foo.php will exclude fileafoo.php and will not
  388.      *           exclude fileaafoo.php.  test/ will exclude all directories and subdirectories of
  389.      *           ANY directory named test encountered in directory parsing.  *test* will exclude
  390.      *           all files and directories that contain test in their name
  391.      * - include: an array of filenames, directory names, or wildcard expressions specifying
  392.      *            files to include in the listing.  All other files will be ignored.
  393.      *            Wildcards are in the same format as ignore
  394.      * - roles: this is an array mapping file extension to install role.  This
  395.      *          specifies default behavior that can be overridden by the exceptions
  396.      *          option and dir_roles option.  use {@link addRole()} to add a new
  397.      *          role to the pre-existing array
  398.      * - dir_roles: this is an array mapping directory name to install role.  All
  399.      *              files in a directory whose name matches the directory will be
  400.      *              given the install role specified.  Single files can be excluded
  401.      *              from this using the exceptions option.  The directory should be
  402.      *              a relative path from the baseinstalldir, or "/" for the baseinstalldir
  403.      * - exceptions: specify file role for specific files.  This array maps all files
  404.      *               matching the exact name of a file to a role as in "file.ext" => "role"
  405.      * - deps: dependency array.  Pass in an empty array to clear all dependencies, and use
  406.      *         {@link addDependency()} to add new ones/replace existing ones
  407.      * - maintainers: maintainers array.  Pass in an empty array to clear all maintainers, and
  408.      *                use {@link addMaintainer()} to add a new maintainer/replace existing maintainer
  409.      * - installexceptions: array mapping of specific filenames to baseinstalldir values.  Use
  410.      *                      this to force the installation of a file into another directory,
  411.      *                      such as forcing a script to be in the root scripts directory so that
  412.      *                      it will be in the path.  The filename must be a relative path to the
  413.      *                      packagedirectory
  414.      * - platformexceptions: array mapping of specific filenames to the platform they should be
  415.      *                       installed on.  Use this to specify unix-only files or windows-only
  416.      *                       files.  The format of the platform string must be
  417.      *                       OS-version-cpu-extra if any more specific information is needed,
  418.      *                       and the OS must be in lower case as in "windows."  The match is
  419.      *                       performed using a regular expression, but uses * and ? wildcards
  420.      *                       instead of .* and .?.  Note that hpux/aix/irix/linux are all
  421.      *                       exclusive.  To select non-windows, use (*ix|*ux)
  422.      * - scriptphaseexceptions: array mapping of scripts to their install phase.  This can be
  423.      *                          one of: pre-install, post-install, pre-uninstall, post-uninstall,
  424.      *                          pre-build, post-build, pre-setup, or post-setup
  425.      * - installas: array mapping of specific filenames to the filename they should be installed as.
  426.      *              Use this to specify new filenames for files that should be installed.  This will
  427.      *              often be used in conjunction with platformexceptions if there are two files for
  428.      *              different OSes that must have the same name when installed.
  429.      * - replacements: array mapping of specific filenames to complex text search-and-replace that
  430.      *                 should be performed upon install.  The format is:
  431.      *   <pre>
  432.      *   filename => array('type' => php-const|pear-config|package-info
  433.      *                     'from' => text in file
  434.      *                     'to' => name of variable)
  435.      *   </pre>
  436.      *                 if type is php-const, then 'to' must be the name of a PHP Constant.
  437.      *                 If type is pear-config, then 'to' must be the name of a PEAR config
  438.      *                 variable accessible through a PEAR_Config class->get() method.  If
  439.      *                 type is package-info, then 'to' must be the name of a section from
  440.      *                 the package.xml file used to install this file.
  441.      * - configure_options: array specifies build options for PECL packages (you should probably
  442.      *                      use PECL_Gen instead, but it's here for completeness)
  443.      * @see PEAR_PackageFileManager_File
  444.      * @see PEAR_PackageFileManager_CVS
  445.      * @return void|PEAR_Error
  446.      * @throws PEAR_PACKAGEFILEMANAGER_NOSTATE
  447.      * @throws PEAR_PACKAGEFILEMANAGER_NOVERSION
  448.      * @throws PEAR_PACKAGEFILEMANAGER_NOPKGDIR
  449.      * @throws PEAR_PACKAGEFILEMANAGER_NOBASEDIR
  450.      * @throws PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND_ANYWHERE
  451.      * @throws PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND
  452.      * @param array
  453.      */
  454.     function setOptions($options = array())
  455.     {
  456.         if (!isset($options['state'])) {
  457.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOSTATE);
  458.         }
  459.         if (!isset($options['version'])) {
  460.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOVERSION);
  461.         }
  462.         if (!isset($options['packagedirectory'])) {
  463.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOPKGDIR);
  464.         } else {
  465.             $options['packagedirectory'] = str_replace(DIRECTORY_SEPARATOR,
  466.                                                      '/',
  467.                                                      realpath($options['packagedirectory']));
  468.             if ($options['packagedirectory']{strlen($options['packagedirectory']) - 1} != '/') {
  469.                 $options['packagedirectory'] .= '/';
  470.             }
  471.         }
  472.         if (isset($options['pathtopackagefile'])) {
  473.             $options['pathtopackagefile'] = str_replace(DIRECTORY_SEPARATOR,
  474.                                                      '/',
  475.                                                      realpath($options['pathtopackagefile']));
  476.             if ($options['pathtopackagefile']{strlen($options['pathtopackagefile']) - 1} != '/') {
  477.                 $options['pathtopackagefile'] .= '/';
  478.             }
  479.         }
  480.         if (!isset($options['baseinstalldir'])) {
  481.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOBASEDIR);
  482.         }
  483.         $this->_options = array_merge($this->_options, $options);
  484.         
  485.         if (!class_exists($this->_options['pearcommonclass'])) {
  486.             if ($this->_options['simpleoutput']) {
  487.                 require_once 'PEAR/PackageFileManager/XMLOutput.php';
  488.                 $this->_options['pearcommonclass'] = 'PEAR_PackageFileManager_XMLOutput';
  489.             } else {
  490.                 $this->_options['pearcommonclass'] = 'PEAR_Common';
  491.             }
  492.         }
  493.         $path = ($this->_options['pathtopackagefile'] ?
  494.                     $this->_options['pathtopackagefile'] : $this->_options['packagedirectory']);
  495.         $this->_options['filelistgenerator'] = ucfirst(strtolower($this->_options['filelistgenerator']));
  496.         if (PEAR::isError($res = $this->_getExistingPackageXML($path, $this->_options['packagefile']))) {
  497.             return $res;
  498.         }
  499.         if (!class_exists('PEAR_PackageFileManager_' . $this->_options['filelistgenerator'])) {
  500.             // attempt to load the interface from the standard PEAR location
  501.             if ($this->isIncludeable('PEAR/PackageFileManager/' . $this->_options['filelistgenerator'] . '.php')) {
  502.                 include_once('PEAR/PackageFileManager/' . $this->_options['filelistgenerator'] . '.php');
  503.             } elseif (isset($this->_options['usergeneratordir'])) {
  504.                 // attempt to load from a user-specified directory
  505.                 if (is_dir(realpath($this->_options['usergeneratordir']))) {
  506.                     $this->_options['usergeneratordir'] =
  507.                         str_replace(DIRECTORY_SEPARATOR,
  508.                                     '/',
  509.                                     realpath($this->_options['usergeneratordir']));
  510.                     if ($this->_options['usergeneratordir']{strlen($this->_options['usergeneratordir']) - 1} != '/') {
  511.                         $this->_options['usergeneratordir'] .= '/';
  512.                     }
  513.                 } else {
  514.                     $this->_options['usergeneratordir'] = '////';
  515.                 }
  516.                 if (file_exists($this->_options['usergeneratordir'] .
  517.                       $this->_options['filelistgenerator'] . '.php') &&
  518.                       is_readable($this->_options['usergeneratordir'] .
  519.                       $this->_options['filelistgenerator'] . '.php')) {
  520.                     include_once($this->_options['usergeneratordir'] .
  521.                         $this->_options['filelistgenerator'] . '.php');
  522.                 }
  523.                 if (!class_exists('PEAR_PackageFileManager_' . $this->_options['filelistgenerator'])) {
  524.                     return $this->raiseError(PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND_ANYWHERE,
  525.                             'PEAR_PackageFileManager_' . $this->_options['filelistgenerator']);
  526.                 }
  527.             } else {
  528.                 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_GENERATOR_NOTFOUND,
  529.                         'PEAR_PackageFileManager_' . $this->_options['filelistgenerator']);
  530.             }
  531.         }
  532.     }
  533.     
  534.     /**
  535.      * Add an extension/role mapping to the role mapping option
  536.      *
  537.      * Roles influence both where a file is installed and how it is installed.
  538.      * Files with role="data" are in a completely different directory hierarchy
  539.      * from the program files of role="php"
  540.      * 
  541.      * In PEAR 1.3b2, these roles are
  542.      * - php (most common)
  543.      * - data
  544.      * - doc
  545.      * - test
  546.      * - script (gives the file an executable attribute)
  547.      * - src
  548.      * @param string file extension
  549.      * @param string role
  550.      * @throws PEAR_PACKAGEFILEMANAGER_INVALID_ROLE
  551.      */
  552.     function addRole($extension, $role)
  553.     {
  554.         $roles = call_user_func(array($this->_options['pearcommonclass'], 'getfileroles'));
  555.         if (!in_array($role, $roles)) {
  556.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_INVALID_ROLE, implode($roles, ', '), $role);
  557.         }
  558.         $this->_options['roles'][$extension] = $role;
  559.     }
  560.     
  561.     /**
  562.      * Add an install-time platform conditional install for a file
  563.      *
  564.      * The format of the platform string must be
  565.      * OS-version-cpu-extra if any more specific information is needed,
  566.      * and the OS must be in lower case as in "windows."  The match is
  567.      * performed using a regular expression, but uses * and ? wildcards
  568.      * instead of .* and .?.  Note that hpux/aix/irix/linux are all
  569.      * exclusive.  To select non-windows, use (*ix|*ux)
  570.      *
  571.      * This information is based on eyeing the source for OS/Guess.php, so
  572.      * if you are unsure of what to do, read that file.
  573.      * @param string relative path of file (relative to packagedirectory option)
  574.      * @param string platform descriptor string
  575.      */
  576.     function addPlatformException($path, $platform)
  577.     {
  578.         if (!isset($this->_options['platformexceptions'])) {
  579.             $this->_options['platformexceptions'] = array();
  580.         }
  581.         $this->_options['platformexceptions'][$path] = $platform;
  582.     }
  583.     
  584.     /**
  585.      * Add a replacement option for a file
  586.      *
  587.      * This sets an install-time complex search-and-replace function
  588.      * allowing the setting of platform-specific variables in an
  589.      * installed file.
  590.      *
  591.      * if $type is php-const, then $to must be the name of a PHP Constant.
  592.      * If $type is pear-config, then $to must be the name of a PEAR config
  593.      * variable accessible through a {@link PEAR_Config::get()} method.  If
  594.      * type is package-info, then $to must be the name of a section from
  595.      * the package.xml file used to install this file.
  596.      * @param string relative path of file (relative to packagedirectory option)
  597.      * @param string variable type, either php-const, pear-config or package-info
  598.      * @param string text to replace in the source file
  599.      * @param string variable name to use for replacement
  600.      * @throws PEAR_PACKAGEFILEMANAGER_INVALID_REPLACETYPE
  601.      */
  602.     function addReplacement($path, $type, $from, $to)
  603.     {
  604.         if (!isset($this->_options['replacements'])) {
  605.             $this->_options['replacements'] = array();
  606.         }
  607.         $types = call_user_func(array($this->_options['pearcommonclass'], 'getreplacementtypes'));
  608.         if (!in_array($type, $types)) {
  609.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_INVALID_REPLACETYPE,
  610.                 implode($types, ', '), $type);
  611.         }
  612.         $this->_options['replacements'][$path][] = array('type' => $type, 'from' => $from, 'to' => $to);
  613.     }
  614.     
  615.     /**
  616.      * Add a maintainer to the list of maintainers.
  617.      *
  618.      * Every maintainer must have a valid account at pear.php.net.  The
  619.      * first parameter is the account name (for instance, cellog is the
  620.      * handle for Greg Beaver at pear.php.net).  Every maintainer has
  621.      * one of four possible roles:
  622.      * - lead: the primary maintainer
  623.      * - developer: an important developer on the project
  624.      * - contributor: self-explanatory
  625.      * - helper: ditto
  626.      *
  627.      * Finally, specify the name and email of the maintainer
  628.      * @param string username on pear.php.net of maintainer
  629.      * @param lead|developer|contributor|helper role of maintainer
  630.      * @param string full name of maintainer
  631.      * @param string email address of maintainer
  632.      */
  633.     function addMaintainer($handle, $role, $name, $email)
  634.     {
  635.         if (!$this->_packageXml) {
  636.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS);
  637.         }
  638.         if (!in_array($role, $GLOBALS['_PEAR_Common_maintainer_roles'])) {
  639.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_WRONG_MROLE,
  640.                 implode(', ', call_user_func(array($this->_options['pearcommonclass'],
  641.                     'getUserRoles'))),
  642.                 $role);
  643.         }
  644.         if (!isset($this->_packageXml['maintainers'])) {
  645.             $this->_packageXml['maintainers'] = array();
  646.         }
  647.         $found = false;
  648.         foreach($this->_packageXml['maintainers'] as $index => $maintainer) {
  649.             if ($maintainer['handle'] == $handle) {
  650.                 $found = $index;
  651.                 break;
  652.             }
  653.         }
  654.         $maintainer =
  655.             array('handle' => $handle, 'role' => $role, 'name' => $name, 'email' => $email);
  656.         if ($found !== false) {
  657.             $this->_packageXml['maintainers'][$found] = $maintainer;
  658.         } else {
  659.             $this->_packageXml['maintainers'][] = $maintainer;
  660.         }
  661.     }
  662.     
  663.     /**
  664.      * Add an install-time configuration option for building of source
  665.      *
  666.      * This option is only useful to PECL projects that are built upon
  667.      * installation
  668.      * @param string name of the option
  669.      * @param string prompt to display to the user
  670.      * @param string default value
  671.      * @throws PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS
  672.      * @return void|PEAR_Error
  673.      */
  674.     function addConfigureOption($name, $prompt, $default = null)
  675.     {
  676.         if (!$this->_packageXml) {
  677.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS);
  678.         }
  679.         if (!isset($this->_packageXml['configure_options'])) {
  680.             $this->_packageXml['configure_options'] = array();
  681.         }
  682.         $found = false;
  683.         foreach($this->_packageXml['configure_options'] as $index => $option) {
  684.             if ($option['name'] == $name) {
  685.                 $found = $index;
  686.                 break;
  687.             }
  688.         }
  689.         $option = array('name' => $name, 'prompt' => $prompt);
  690.         if (isset($default)) {
  691.             $option['default'] = $default;
  692.         }
  693.         if ($found !== false) {
  694.             $this->_packageXml['configure_options'][$found] = $option;
  695.         } else {
  696.             $this->_packageXml['configure_options'][] = $option;
  697.         }
  698.     }
  699.     
  700.     /**
  701.      * Add a dependency on another package, or an extension/php
  702.      *
  703.      * This will overwrite an existing dependency if it is found.  In
  704.      * other words, if a dependency on PHP 4.1.0 exists, and
  705.      * addDependency('php', '4.3.0', 'ge', 'php') is called, the existing
  706.      * dependency on PHP 4.1.0 will be overwritten with the new one on PHP 4.3.0
  707.      * @param string Dependency element name
  708.      * @param string Dependency version
  709.      * @param string A specific operator for the version, this can be one of:
  710.      *   'has', 'not', 'lt', 'le', 'eq', 'ne', 'ge', or 'gt'
  711.      * @param string Dependency type.  This can be one of:
  712.      *   'pkg', 'ext', 'php', 'prog', 'os', 'sapi', or 'zend'
  713.      * @param boolean true if dependency is optional
  714.      * @throws PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS
  715.      * @throws PEAR_PACKAGEFILEMANAGER_PHP_NOT_PACKAGE
  716.      * @return void|PEAR_Error
  717.      */
  718.     function addDependency($name, $version = false, $operator = 'ge', $type = 'pkg', $optional = false)
  719.     {
  720.         if (!$this->_packageXml) {
  721.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS);
  722.         }
  723.         if ((strtolower($name) == 'php') && (strtolower($type) == 'pkg')) {
  724.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_PHP_NOT_PACKAGE);
  725.         }
  726.         if (!isset($this->_packageXml['release_deps'])) {
  727.             $this->_packageXml['release_deps'] = array();
  728.         }
  729.         $found = false;
  730.         foreach($this->_packageXml['release_deps'] as $index => $dep) {
  731.             if ($type == 'php') {
  732.                 if ($dep['type'] == 'php') {
  733.                     $found = $index;
  734.                     break;
  735.                 }
  736.             } else {
  737.                 if (isset($dep['name']) && $dep['name'] == $name && $dep['type'] == $type) {
  738.                     $found = $index;
  739.                     break;
  740.                 }
  741.             }
  742.         }
  743.         $dep =
  744.             array(
  745.                 'name' => $name,
  746.                 'type' => $type);
  747.         if ($type == 'php') {
  748.             unset($dep['name']);
  749.         }
  750.         if ($operator) {
  751.             $dep['rel'] = $operator;
  752.             if ($dep['rel'] != 'has' && $version) {
  753.                 $dep['version'] = $version;
  754.             }
  755.         }
  756.         
  757.         if ($optional) {
  758.             $dep['optional'] = 'yes';
  759.         } else {
  760.             $dep['optional'] = 'no';
  761.         }
  762.  
  763.         if ($found !== false) {
  764.             $this->_packageXml['release_deps'][$found] = $dep; // overwrite existing dependency
  765.         } else {
  766.             $this->_packageXml['release_deps'][] = $dep; // add new dependency
  767.         }
  768.     }
  769.  
  770.     /**
  771.      * Writes the package.xml file out with the newly created <release></release> tag
  772.      *
  773.      * ALWAYS use {@link debugPackageFile} to verify that output is correct before
  774.      * overwriting your package.xml
  775.      * @param boolean null if no debugging, true if web interface, false if command-line
  776.      * @throws PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS
  777.      * @throws PEAR_PACKAGEFILEMANAGER_ADD_MAINTAINERS
  778.      * @throws PEAR_PACKAGEFILEMANAGER_CANTWRITE_PKGFILE
  779.      * @throws PEAR_PACKAGEFILEMANAGER_CANTCOPY_PKGFILE
  780.      * @throws PEAR_PACKAGEFILEMANAGER_CANTOPEN_TMPPKGFILE
  781.      * @throws PEAR_PACKAGEFILEMANAGER_DEST_UNWRITABLE
  782.      * @return void|PEAR_Error
  783.      */
  784.     function writePackageFile($debuginterface = null)
  785.     {
  786.         if (!$this->_packageXml) {
  787.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS);
  788.         }
  789.         if (!isset($this->_packageXml['maintainers']) || empty($this->_packageXml['maintainers'])) {
  790.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_ADD_MAINTAINERS);
  791.         }
  792.         extract($this->_options);
  793.         $date = date('Y-m-d');
  794.         if (isset($package)) {
  795.             $this->_packageXml['package'] = $package;
  796.         }
  797.         if (isset($summary)) {
  798.             $this->_packageXml['summary'] = $summary;
  799.         }
  800.         if (isset($description)) {
  801.             $this->_packageXml['description'] = $description;
  802.         }
  803.         $this->_packageXml['release_date'] = $date;
  804.         $this->_packageXml['version'] = $version;
  805.         $this->_packageXml['release_license'] = $license;
  806.         $this->_packageXml['release_state'] = $state;
  807.         $this->_packageXml['release_notes'] = $notes;
  808.         $PEAR_Common = $this->_options['pearcommonclass'];
  809.         $this->_pear = new $PEAR_Common;
  810.         $this->_packageXml['filelist'] = $this->_getFileList();
  811.         $warnings = $this->getWarnings();
  812.         if (count($warnings)) {
  813.             $nl = (isset($debuginterface) && $debuginterface ? '<br />' : "\n");
  814.             foreach($warnings as $errmsg) {
  815.                 echo 'WARNING: ' . $errmsg['message'] . $nl;
  816.             }
  817.         }
  818.         if (PEAR::isError($this->_packageXml['filelist'])) {
  819.             return $this->_packageXml['filelist'];
  820.         }
  821.         if (isset($this->_pear->pkginfo['provides'])) {
  822.             $this->_packageXml['provides'] = $this->_pear->pkginfo['provides'];
  823.         }
  824.         if ($this->_options['simpleoutput']) {
  825.             unset($this->_packageXml['provides']);
  826.         }
  827.         $this->_packageXml['release_deps'] = $this->_getDependencies();
  828.         $this->_updateChangeLog();
  829.         
  830.         $common = &$this->_pear;
  831.         $warnings = $errors = array();
  832.         $packagexml = $common->xmlFromInfo($this->_packageXml);
  833.         $common->validatePackageInfo($packagexml, $warnings, $errors, $this->_options['packagedirectory']);
  834.         if (count($errors)) {
  835.             $ret = '';
  836.             $nl = (isset($debuginterface) && $debuginterface ? '<br />' : "\n");
  837.             foreach($errors as $errmsg) {
  838.                 $ret .= $errmsg . $nl;
  839.             }
  840.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_INVALID_PACKAGE, $nl, $ret);
  841.         }
  842.         if (count($warnings)) {
  843.             $nl = (isset($debuginterface) && $debuginterface ? '<br />' : "\n");
  844.             foreach($warnings as $errmsg) {
  845.                 echo $errmsg . $nl;
  846.             }
  847.         }
  848.         if (!strpos($packagexml, '<!DOCTYPE')) {
  849.             // hack to fix pear
  850.             $packagexml = str_replace('<package version="1.0">',
  851.                 '<!DOCTYPE package SYSTEM "' . $this->_options['doctype'] .
  852.                 "\">\n<package version=\"1.0\">",
  853.                 $packagexml);
  854.         }
  855.         if (isset($debuginterface)) {
  856.             if ($debuginterface) {
  857.                 echo '<pre>' . htmlentities($packagexml) . '</pre>';
  858.             } else {
  859.                 echo $packagexml;
  860.             }
  861.             return true;
  862.         }
  863.         $outputdir = ($this->_options['outputdirectory'] ?
  864.                         $this->_options['outputdirectory'] : $this->_options['packagedirectory']);
  865.         if ((file_exists($outputdir . $this->_options['packagefile']) &&
  866.                 is_writable($outputdir . $this->_options['packagefile']))
  867.                 ||
  868.                 @touch($outputdir . $this->_options['packagefile'])) {
  869.             if ($fp = @fopen($outputdir . $this->_options['packagefile'] . '.tmp', "w")) {
  870.                 $written = @fwrite($fp, $packagexml);
  871.                 @fclose($fp);
  872.                 if ($written === false) {
  873.                     return $this->raiseError(PEAR_PACKAGEFILEMANAGER_CANTWRITE_PKGFILE);
  874.                 }
  875.                 if (!@copy($outputdir . $this->_options['packagefile'] . '.tmp',
  876.                         $outputdir . $this->_options['packagefile'])) {
  877.                     return $this->raiseError(PEAR_PACKAGEFILEMANAGER_CANTCOPY_PKGFILE);
  878.                 } else {
  879.                     @unlink($outputdir . $this->_options['packagefile'] . '.tmp');
  880.                     return true;
  881.                 }
  882.             } else {
  883.                 return $this->raiseError(PEAR_PACKAGEFILEMANAGER_CANTOPEN_TMPPKGFILE,
  884.                     $outputdir . $this->_options['packagefile'] . '.tmp');
  885.             }
  886.         } else {
  887.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_DEST_UNWRITABLE, $outputdir);
  888.         }
  889.     }
  890.     
  891.     /**
  892.      * ALWAYS use this to test output before overwriting your package.xml!!
  893.      *
  894.      * This method instructs writePackageFile() to simply print the package.xml
  895.      * to output, either command-line or web-friendly (this is automatic
  896.      * based on the existence of $_SERVER['PATH_TRANSLATED']
  897.      * @uses writePackageFile() calls with the debug parameter set based on
  898.      *       whether it is called from the command-line or web interface
  899.      */
  900.     function debugPackageFile()
  901.     {
  902.         $webinterface = isset($_SERVER['PATH_TRANSLATED']);
  903.         return $this->writePackageFile($webinterface);
  904.     }
  905.     
  906.     /**
  907.      * Store a warning on the warning stack
  908.      */
  909.     function pushWarning($code, $info)
  910.     {
  911.         $this->_warningStack[] = array('code' => $code,
  912.                                        'message' => $this->_getMessage($code, $info));
  913.     }
  914.     
  915.     /**
  916.      * Retrieve the list of warnings
  917.      * @return array
  918.      */
  919.     function getWarnings()
  920.     {
  921.         $a = $this->_warningStack;
  922.         $this->_warningStack = array();
  923.         return $a;
  924.     }
  925.     
  926.     /**
  927.      * Retrieve an error message from a code
  928.      * @access private
  929.      * @return string Error message
  930.      */
  931.     function _getMessage($code, $info)
  932.     {
  933.         $msg = $GLOBALS['_PEAR_PACKAGEFILEMANAGER_ERRORS'][$this->_options['lang']][$code];
  934.         foreach ($info as $name => $value) {
  935.             $msg = str_replace('%' . $name . '%', $value, $msg);
  936.         }
  937.         return $msg;
  938.     }
  939.     
  940.     /**
  941.      * Utility function to shorten error generation code
  942.      *
  943.      * {@source}
  944.      * @return PEAR_Error
  945.      * @static
  946.      */
  947.     function raiseError($code, $i1 = '', $i2 = '')
  948.     {
  949.         return PEAR::raiseError('PEAR_PackageFileManager Error: ' .
  950.                     sprintf($GLOBALS['_PEAR_PACKAGEFILEMANAGER_ERRORS'][$this->_options['lang']][$code],
  951.                     $i1, $i2), $code);
  952.     }
  953.     
  954.     /**
  955.      * Uses {@link PEAR_Common::analyzeSourceCode()} and {@link PEAR_Common::buildProvidesArray()}
  956.      * to create the <provides></provides> section of the package.xml
  957.      * @param PEAR_Common
  958.      * @param string path to source file
  959.      * @access private
  960.      */
  961.     function _addProvides(&$pear, $file)
  962.     {
  963.         if (!($a = $pear->analyzeSourceCode($file))) {
  964.             return;
  965.         } else {
  966.             $pear->buildProvidesArray($a);
  967.         }
  968.     }
  969.     
  970.     /**
  971.      * @uses getDirTag() generate the xml from the array
  972.      * @return string
  973.      * @access private
  974.      */
  975.     function _getFileList()
  976.     {
  977.         $generatorclass = 'PEAR_PackageFileManager_' . $this->_options['filelistgenerator'];
  978.         $generator = new $generatorclass($this, $this->_options);
  979.         if ($this->_options['simpleoutput']) {
  980.             return $this->_getSimpleDirTag($generator->getFileList());
  981.         }
  982.         return $this->_getDirTag($generator->getFileList()); 
  983.     }
  984.     
  985.     /**
  986.      * Recursively generate the <filelist> section's <dir> and <file> tags, but with
  987.      * simple human-readable output
  988.      * @param array|PEAR_Error the sorted directory structure, or an error
  989.      *                         from filelist generation
  990.      * @param false|string whether the parent directory has a role this should
  991.      * inherit
  992.      * @param integer indentation level
  993.      * @return array|PEAR_Error
  994.      * @access private
  995.      */
  996.     function _getSimpleDirTag($struc, $role = false, $_curdir = '')
  997.     {
  998.         if (PEAR::isError($struc)) {
  999.             return $struc;
  1000.         }
  1001.         extract($this->_options);
  1002.         $ret = array();
  1003.         foreach($struc as $dir => $files) {
  1004.             if (false && $dir === '/') {
  1005.                 // global directory role? overrides all exceptions except file exceptions
  1006.                 if (isset($dir_roles['/'])) {
  1007.                     $role = $dir_roles['/'];
  1008.                 }
  1009.                 return array(
  1010.                     'baseinstalldir' => $this->_options['baseinstalldir'],
  1011.                     '##files' => $this->_getSimpleDirTag($struc[$dir], $role, ''),
  1012.                     'name' => '/');
  1013.             } else {
  1014.                 if (!isset($files['file'])) {
  1015.                     if (isset($dir_roles[$_curdir . $dir])) {
  1016.                         $myrole = $dir_roles[$_curdir . $dir];
  1017.                     } else {
  1018.                         $myrole = $role;
  1019.                     }
  1020.                     $ret[$dir] = array();
  1021.                     if ($dir == '/') {
  1022.                         $ret[$dir]['baseinstalldir'] = $this->_options['baseinstalldir'];
  1023.                     }
  1024.                     $ret[$dir]['name'] = $dir;
  1025.                     $recurdir = ($_curdir == '') ? $dir . '/' : $_curdir . $dir . '/';
  1026.                     if ($recurdir == '//') {
  1027.                         $recurdir = '';
  1028.                     }
  1029.                     $ret[$dir]['##files'] = $this->_getSimpleDirTag($files, $myrole, $recurdir);
  1030.                 } else {
  1031.                     $myrole = '';
  1032.                     if (!$role)
  1033.                     {
  1034.                         $myrole = false;
  1035.                         if (isset($exceptions[$files['path']])) {
  1036.                             $myrole = $exceptions[$files['path']];
  1037.                         } elseif (isset($roles[$files['ext']])) {
  1038.                             $myrole = $roles[$files['ext']];
  1039.                         } else {
  1040.                             $myrole = $roles['*'];
  1041.                         }
  1042.                     } else {
  1043.                         $myrole = $role;
  1044.                         if (isset($exceptions[$files['path']])) {
  1045.                             $myrole = $exceptions[$files['path']];
  1046.                         }
  1047.                     }
  1048.                     $test = explode('/', $files['path']);
  1049.                     foreach ($test as $subpath) {
  1050.                         if ($subpath == 'CVS') {
  1051.                             $this->pushWarning(PEAR_PACKAGEFILEMANAGER_CVS_PACKAGED, array('path' => $files['path']));
  1052.                         }
  1053.                     }
  1054.                     $ret[$files['file']] = array('role' => $myrole);
  1055.                     if (isset($installexceptions[$files['path']])) {
  1056.                         $ret[$files['file']]['baseinstalldir'] = $installexceptions[$files['path']];
  1057.                     }
  1058.                     if (isset($platformexceptions[$files['path']])) {
  1059.                         $ret[$files['file']]['platform'] = $platformexceptions[$files['path']];
  1060.                     }
  1061.                     if (isset($installas[$files['path']])) {
  1062.                         $ret[$files['file']]['install-as'] = $installas[$files['path']];
  1063.                     }
  1064.                     if (isset($replacements[$files['path']])) {
  1065.                         $ret[$files['file']]['replacements'] = $replacements[$files['path']];
  1066.                     }
  1067.                 }
  1068.             }
  1069.         }
  1070.         return $ret;
  1071.     }
  1072.     
  1073.     /**
  1074.      * Recursively generate the <filelist> section's <dir> and <file> tags
  1075.      * @param array|PEAR_Error the sorted directory structure, or an error
  1076.      *                         from filelist generation
  1077.      * @param false|string whether the parent directory has a role this should
  1078.      * inherit
  1079.      * @param integer indentation level
  1080.      * @return array|PEAR_Error
  1081.      * @access private
  1082.      */
  1083.     function _getDirTag($struc, $role=false, $_curdir = '')
  1084.     {
  1085.         if (PEAR::isError($struc)) {
  1086.             return $struc;
  1087.         }
  1088.         extract($this->_options);
  1089.         $ret = array();
  1090.         foreach($struc as $dir => $files) {
  1091.             if ($dir === '/') {
  1092.                 // global directory role? overrides all exceptions except file exceptions
  1093.                 if (isset($dir_roles['/'])) {
  1094.                     $role = $dir_roles['/'];
  1095.                 }
  1096.                 return $this->_getDirTag($struc[$dir], $role, '');
  1097.             } else {
  1098.                 if (!isset($files['file'])) {
  1099.                     $myrole = '';
  1100.                     if (isset($dir_roles[$_curdir . $dir])) {
  1101.                         $myrole = $dir_roles[$_curdir . $dir];
  1102.                     } elseif ($role) {
  1103.                         $myrole = $role;
  1104.                     }
  1105.                     $ret = array_merge($ret, $this->_getDirTag($files, $myrole, $_curdir . $dir . '/'));
  1106.                 } else {
  1107.                     $myrole = '';
  1108.                     if (!$role)
  1109.                     {
  1110.                         $myrole = false;
  1111.                         if (isset($exceptions[$files['path']])) {
  1112.                             $myrole = $exceptions[$files['path']];
  1113.                         } elseif (isset($roles[$files['ext']])) {
  1114.                             $myrole = $roles[$files['ext']];
  1115.                         } else {
  1116.                             $myrole = $roles['*'];
  1117.                         }
  1118.                     } else {
  1119.                         $myrole = $role;
  1120.                         if (isset($exceptions[$files['path']])) {
  1121.                             $myrole = $exceptions[$files['path']];
  1122.                         }
  1123.                     }
  1124.                     if (isset($installexceptions[$files['path']])) {
  1125.                         $bi = $installexceptions[$files['path']];
  1126.                     } else {
  1127.                         $bi = $this->_options['baseinstalldir'];
  1128.                     }
  1129.                     $test = explode('/', $files['path']);
  1130.                     foreach ($test as $subpath) {
  1131.                         if ($subpath == 'CVS') {
  1132.                             $this->pushWarning(PEAR_PACKAGEFILEMANAGER_CVS_PACKAGED, array('path' => $files['path']));
  1133.                         }
  1134.                     }
  1135.                     $ret[$files['path']] =
  1136.                         array('role' => $myrole,
  1137.                               'baseinstalldir' => $bi,
  1138.                               );
  1139.                     $md5sum = @md5_file($this->_options['packagedirectory'] . $files['path']);
  1140.                     if (!empty($md5sum)) {
  1141.                         $ret[$files['path']]['md5sum'] = $md5sum;
  1142.                     }
  1143.                     if ($this->_options['simpleoutput']) {
  1144.                         unset($ret[$files['path']]['md5sum']);
  1145.                     }
  1146.                     if (isset($platformexceptions[$files['path']])) {
  1147.                         $ret[$files['path']]['platform'] = $platformexceptions[$files['path']];
  1148.                     }
  1149.                     if (isset($installas[$files['path']])) {
  1150.                         $ret[$files['path']]['install-as'] = $installas[$files['path']];
  1151.                     }
  1152.                     if (isset($replacements[$files['path']])) {
  1153.                         $ret[$files['path']]['replacements'] = $replacements[$files['path']];
  1154.                     }
  1155.                     if ($myrole == 'php' && !$this->_options['simpleoutput']) {
  1156.                         $this->_addProvides($this->_pear, $files['fullpath']);
  1157.                     }
  1158.                 }
  1159.             }
  1160.         }
  1161.         return $ret;
  1162.     }
  1163.  
  1164.     /**
  1165.      * Retrieve the 'deps' option passed to the constructor
  1166.      * @access private
  1167.      * @return array
  1168.      */
  1169.     function _getDependencies()
  1170.     {
  1171.         if (isset($this->_packageXml['release_deps']) &&
  1172.               is_array($this->_packageXml['release_deps'])) {
  1173.             return $this->_packageXml['release_deps'];
  1174.         } else {
  1175.             return array();
  1176.         }
  1177.     }
  1178.  
  1179.     /**
  1180.      * Creates a changelog entry with the current release
  1181.      * notes and dates, or overwrites a previous creation
  1182.      * @access private
  1183.      */
  1184.     function _updateChangeLog()
  1185.     {
  1186.         $curlog = $oldchangelog = false;
  1187.         if (!isset($this->_packageXml['changelog'])) {
  1188.             $changelog = array();
  1189.             if (isset($this->_oldPackageXml['release_notes'])) {
  1190.                 $changelog['release_notes'] = $this->_oldPackageXml['release_notes'];
  1191.             }
  1192.             if (isset($this->_oldPackageXml['version'])) {
  1193.                 $changelog['version'] = $this->_oldPackageXml['version'];
  1194.             }
  1195.             if (isset($this->_oldPackageXml['release_date'])) {
  1196.                 $changelog['release_date'] = $this->_oldPackageXml['release_date'];
  1197.             }
  1198.             if (isset($this->_oldPackageXml['release_license'])) {
  1199.                 $changelog['release_license'] = $this->_oldPackageXml['release_license'];
  1200.             }
  1201.             if (isset($this->_oldPackageXml['release_state'])) {
  1202.                 $changelog['release_state'] = $this->_oldPackageXml['release_state'];
  1203.             }
  1204.             if (count($changelog)) {
  1205.                 $this->_packageXml['changelog'] = array($changelog);
  1206.             } else {
  1207.                 $this->_packageXml['changelog'] = array();
  1208.             }
  1209.         } else {
  1210.             if (isset($this->_oldPackageXml['release_notes'])) {
  1211.                 $oldchangelog['release_notes'] = $this->_oldPackageXml['release_notes'];
  1212.             }
  1213.             if (isset($this->_oldPackageXml['version'])) {
  1214.                 $oldchangelog['version'] = $this->_oldPackageXml['version'];
  1215.             }
  1216.             if (isset($this->_oldPackageXml['release_date'])) {
  1217.                 $oldchangelog['release_date'] = $this->_oldPackageXml['release_date'];
  1218.             }
  1219.             if (isset($this->_oldPackageXml['release_license'])) {
  1220.                 $oldchangelog['release_license'] = $this->_oldPackageXml['release_license'];
  1221.             }
  1222.             if (isset($this->_oldPackageXml['release_state'])) {
  1223.                 $oldchangelog['release_state'] = $this->_oldPackageXml['release_state'];
  1224.             }
  1225.         }
  1226.         $hasoldversion = false;
  1227.         foreach($this->_packageXml['changelog'] as $index => $changelog) {
  1228.             if ($oldchangelog && isset($oldchangelog['version']) 
  1229.                     && strnatcasecmp($oldchangelog['version'], $changelog['version']) == 0) {
  1230.                 $hasoldversion = true;
  1231.             }
  1232.             if (isset($changelog['version']) && strnatcasecmp($changelog['version'], $this->_options['version']) == 0) {
  1233.                 $curlog = $index;
  1234.             }
  1235.             if (isset($this->_packageXml['changelog'][$index]['release_notes'])) {
  1236.                 $this->_packageXml['changelog'][$index]['release_notes'] = trim($changelog['release_notes']);
  1237.             }
  1238.             // the parsing of the release notes adds a \n for some reason
  1239.         }
  1240.         if (!$hasoldversion && $oldchangelog && count($oldchangelog)
  1241.               && $oldchangelog['version'] != $this->_options['version']) {
  1242.             $this->_packageXml['changelog'][] = $oldchangelog;
  1243.         }
  1244.         $notes = ($this->_options['changelognotes'] ?
  1245.                     $this->_options['changelognotes'] : $this->_options['notes']);
  1246.         $changelog = array('version' => $this->_options['version'],
  1247.                            'release_date' => date('Y-m-d'),
  1248.                            'release_license' => $this->_options['license'],
  1249.                            'release_state' => $this->_options['state'],
  1250.                            'release_notes' => $notes,
  1251.                            );
  1252.         if ($curlog !== false) {
  1253.             $this->_packageXml['changelog'][$curlog] = $changelog;
  1254.         } else {
  1255.             $this->_packageXml['changelog'][] = $changelog;
  1256.         }
  1257.         usort($this->_packageXml['changelog'], array($this, '_changelogsort'));
  1258.     }
  1259.     
  1260.     /**
  1261.      * @static
  1262.      * @access private
  1263.      */
  1264.     function _changelogsort($a, $b)
  1265.     {
  1266.         if ($this->_options['changelogoldtonew']) {
  1267.             $c = strtotime($a['release_date']);
  1268.             $d = strtotime($b['release_date']);
  1269.             $v1 = $a['version'];
  1270.             $v2 = $b['version'];
  1271.          } else {
  1272.             $d = strtotime($a['release_date']);
  1273.             $c = strtotime($b['release_date']);
  1274.             $v2 = $a['version'];
  1275.             $v1 = $b['version'];
  1276.         }
  1277.         if ($c - $d > 0) {
  1278.             return 1;
  1279.         } elseif ($c - $d < 0) {
  1280.             return -1;
  1281.          }
  1282.         return version_compare($v1, $v2);
  1283.     }
  1284.  
  1285.     /**
  1286.      * @return true|PEAR_Error
  1287.      * @uses _generateNewPackageXML() if no package.xml is found, it
  1288.      *       calls this to create a new one
  1289.      * @param string full path to package file
  1290.      * @param string name of package file
  1291.      * @throws PEAR_PACKAGEFILEMANAGER_PATH_DOESNT_EXIST
  1292.      * @access private
  1293.      */
  1294.     function _getExistingPackageXML($path, $packagefile = 'package.xml')
  1295.     {
  1296.         if (is_string($path) && is_dir($path)) {
  1297.             $contents = false;
  1298.             if (file_exists($path . $packagefile)) {
  1299.                 $contents = file_get_contents($path . $packagefile);
  1300.             }
  1301.             if (!$contents) {
  1302.                 return $this->_generateNewPackageXML();
  1303.             } else {
  1304.                 $PEAR_Common = $this->_options['pearcommonclass'];
  1305.                 if (!class_exists($PEAR_Common)) {
  1306.                     return $this->raiseError(PEAR_PACKAGEFILEMANAGER_RUN_SETOPTIONS);
  1307.                 }
  1308.                 $common = new $PEAR_Common;
  1309.                 $this->_oldPackageXml =
  1310.                 $this->_packageXml = $common->infoFromString($contents);
  1311.                 if (PEAR::isError($this->_packageXml)) {
  1312.                     return $this->_packageXml;
  1313.                 }
  1314.                 if ($this->_options['deps'] !== false) {
  1315.                     $this->_packageXml['release_deps'] = $this->_options['deps'];
  1316.                 } else {
  1317.                     if (isset($this->_packageXml['release_deps'])) {
  1318.                         $this->_options['deps'] = $this->_packageXml['release_deps'];
  1319.                     }
  1320.                 }
  1321.                 if ($this->_options['maintainers'] !== false) {
  1322.                     $this->_packageXml['maintainers'] = $this->_options['maintainers'];
  1323.                 } else {
  1324.                     $this->_options['maintainers'] = $this->_packageXml['maintainers'];
  1325.                 }
  1326.                 unset($this->_packageXml['filelist']);
  1327.                 unset($this->_packageXml['provides']);
  1328.             }
  1329.             return true;
  1330.         } else {
  1331.             if (!is_string($path)) {
  1332.                 $path = gettype($path);
  1333.             }
  1334.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_PATH_DOESNT_EXIST,
  1335.                 $path);
  1336.         }
  1337.     }
  1338.     
  1339.     /**
  1340.      * Create the structure for a new package.xml
  1341.      *
  1342.      * @uses $_packageXml emulates reading in a package.xml
  1343.      *       by using the package, summary and description
  1344.      *       options
  1345.      * @return true|PEAR_Error
  1346.      * @access private
  1347.      */
  1348.     function _generateNewPackageXML()
  1349.     {
  1350.         $this->_oldPackageXml = false;
  1351.         if (!isset($this->_options['package'])) {
  1352.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOPACKAGE);
  1353.         }
  1354.         if (!isset($this->_options['summary'])) {
  1355.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NOSUMMARY);
  1356.         }
  1357.         if (!isset($this->_options['description'])) {
  1358.             return $this->raiseError(PEAR_PACKAGEFILEMANAGER_NODESC);
  1359.         }
  1360.         $this->_packageXml = array();
  1361.         $this->_packageXml['package'] = $this->_options['package'];
  1362.         $this->_packageXml['summary'] = $this->_options['summary'];
  1363.         $this->_packageXml['description'] = $this->_options['description'];
  1364.         $this->_packageXml['changelog'] = array();
  1365.         if ($this->_options['deps'] !== false) {
  1366.             $this->_packageXml['release_deps'] = $this->_options['deps'];
  1367.         } else {
  1368.             $this->_packageXml['release_deps'] = $this->_options['deps'] = array();
  1369.         }
  1370.         if ($this->_options['maintainers'] !== false) {
  1371.             $this->_packageXml['maintainers'] = $this->_options['maintainers'];
  1372.         } else {
  1373.             $this->_packageXml['maintainers'] = $this->_options['maintainers'] = array();
  1374.         }
  1375.         return true;
  1376.     }
  1377.  
  1378.     /**
  1379.      * calls {@link file_exists()} for each value in include_path,
  1380.      * then calls {@link is_readable()} when it finds the file
  1381.      * @param string
  1382.      * @static
  1383.      * @return boolean
  1384.      */
  1385.     function isIncludeable($filename)
  1386.     {
  1387.         $ip = ini_get('include_path');
  1388.         $ip = explode(PATH_SEPARATOR, $ip);
  1389.         foreach($ip as $path)
  1390.         {
  1391.             if ($a = realpath($path . DIRECTORY_SEPARATOR . $filename))
  1392.             {
  1393.                 if (is_readable($a))
  1394.                 {
  1395.                     return true;
  1396.                 }
  1397.             }
  1398.         }
  1399.         return false;
  1400.     }
  1401. }
  1402.  
  1403. if (!function_exists('file_get_contents')) {
  1404. /**
  1405.  * @ignore
  1406.  */
  1407. function file_get_contents($path, $use_include_path = null, $context = null)
  1408. {
  1409.     $a = @file($path, $use_include_path, $context);
  1410.     if (is_array($a)) {
  1411.         return implode('', $a);
  1412.     } else {
  1413.         return false;
  1414.     }
  1415. }
  1416. }
  1417. ?>
  1418.